package com.hzmg.akkacloud.worker.annotation.creater;

import akka.actor.AbstractActor;
import akka.actor.ActorSystem;
import akka.actor.Props;
import cn.hutool.core.util.ObjectUtil;
import com.hzmg.akkacloud.common.exception.AkkaCloudException;
import com.hzmg.akkacloud.common.serialize.SerializerUtils;
import com.hzmg.akkacloud.worker.annotation.RemoteServiceCall;
import com.hzmg.akkacloud.worker.annotation.RemoteServiceClass;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 服务远程调用初始化
 * 目前只支持使用spring的应用。
 * 非spring构建的系统使用http的方式远程调用。
 *
 * @author zbw
 * @since 2022/8/14
 */
@Order(0)
@Component
public class RemoteServiceActorCreator {
    static final Logger logger = LoggerFactory.getLogger(RemoteServiceActorCreator.class);

    public static void initRemoteServiceInvoke(ApplicationContext context, ActorSystem actorSystem) {
        try {
            // 获取所有beanNames
            String[] beanNames = context.getBeanNamesForType(Object.class);
            for (String beanName : beanNames) {
                RemoteServiceClass remoteServiceClass = context.findAnnotationOnBean(beanName,
                        RemoteServiceClass.class);
                //判断该类是否含有RemoteServiceClass注解
                if (remoteServiceClass != null) {
                    Object newInstance = context.getBean(beanName);
                    //newInstance.getClass().getTypeName();
                    Method[] methods = context.getBean(beanName).getClass()
                            .getDeclaredMethods();
                    for (Method method : methods) {
                        //判断该方法是否有RemoteServiceCall注解
                        if (method.isAnnotationPresent(RemoteServiceCall.class)) {
                            RemoteServiceCall remoteServiceCall = method
                                    .getAnnotation(RemoteServiceCall.class);
                            //生成actor
                            actorSystem.actorOf(Actor.props(method, newInstance), remoteServiceCall.actorName());
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new AkkaCloudException(e.toString());
        }
    }

    @AllArgsConstructor
    static class Actor extends AbstractActor {

        Method method;
        Object newInstance;

        public static Props props(Method method, Object newInstance) {
            return Props.create(Actor.class, () -> new Actor(method, newInstance));
        }

        @Override
        public Receive createReceive() {
            return receiveBuilder()
                    .matchAny(this::onReceive)
                    .build();
        }

        void onReceive(Object obj) {
            try {
                if ("void".equals(method.getReturnType().getTypeName())) {
                    getSender().tell("ok", getSelf());
                }
                Object returnObj;
                Object getObj = SerializerUtils.deSerialized((byte[]) obj);
                if (getObj == null) {
                    returnObj = method.invoke(newInstance);
                } else {
                    returnObj = method.invoke(newInstance, getObj);
                }

                if (!ObjectUtil.isEmpty(returnObj)) {
                    getSender().tell(SerializerUtils.serialize(returnObj), getSelf());
                }
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
    }
}




